/* 
 * MODBUS Library: SAM7X Port
 * Copyright (c) 2007 Christian Walter <wolti@sil.at>
 * All rights reserved.
 *
 * $Id: portISR.c,v 1.1 2007/09/12 10:15:56 wolti Exp $
 */
/*
 * FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry.
 *
 * This file is part of the FreeRTOS.org distribution.
 *
 * FreeRTOS.org is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * FreeRTOS.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with FreeRTOS.org; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * A special exception to the GPL can be applied should you wish to distribute
 * a combined work that includes FreeRTOS.org, without being obliged to provide
 * the source code for any proprietary components.  See the licensing section 
 * of http://www.FreeRTOS.org for full details of how and when the exception
 * can be applied.
 * 
 ****************************************************************************
 * See http://www.FreeRTOS.org for documentation, latest information, license 
 * and contact details.  Please ensure to read the configuration and relevant 
 * port sections of the online documentation.
 *
 * Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along
 * with commercial development and support options.
 ***************************************************************************
 */

/* ----------------------- System includes ----------------------------------*/
#include <stdlib.h>

/* ----------------------- FreeRTOS includes --------------------------------*/
#include "FreeRTOS.h"
#include "task.h"

/* ----------------------- Platform includes --------------------------------*/
#include "AT91SAM7X256.h"
#include "lib_AT91SAM7X256.h"

/* ----------------------- Defines ------------------------------------------*/
#define portTIMER_MATCH_ISR_BIT           ( ( unsigned portCHAR ) 0x01 )
#define portCLEAR_VIC_INTERRUPT           ( ( unsigned portLONG ) 0 )
#define portNO_CRITICAL_NESTING           ( ( unsigned portLONG ) 0 )
volatile unsigned portLONG ulCriticalNesting = 9999UL;

/* ----------------------- Type definitions ---------------------------------*/

/* ----------------------- Static variables ---------------------------------*/

/* ----------------------- Function prototypes ------------------------------*/
void            vPortDisableInterruptsFromThumb( void ) __attribute__ ( ( naked ) );
void            vPortEnableInterruptsFromThumb( void ) __attribute__ ( ( naked ) );
void            vNonPreemptiveTick( void ) __attribute__ ( ( interrupt( "IRQ" ) ) );
void            vPreemptiveTick( void ) __attribute__ ( ( naked ) );
void            vPortYieldProcessor( void ) __attribute__ ( ( interrupt( "SWI" ), naked ) );

void            vPortISRStartFirstTask( void );

/* ----------------------- Static functions ---------------------------------*/

/* ----------------------- Start implementation -----------------------------*/

void
vPortISRStartFirstTask( void )
{
    /* Simply start the scheduler.  This is included here as it can only be
     * called from ARM mode. 
     */
    portRESTORE_CONTEXT(  );
}

/*
 * Called by portYIELD() or taskYIELD() to manually force a context switch.
 *
 * When a context switch is performed from the task level the saved task 
 * context is made to look as if it occurred from within the tick ISR.  This
 * way the same restore context function can be used when restoring the context
 * saved from the ISR or that saved from a call to vPortYieldProcessor.
 */
void
vPortYieldProcessor( void )
{
    /* Within an IRQ ISR the link register has an offset from the true return 
     * address, but an SWI ISR does not. Add the offset manually so the same 
     * ISR return code can be used in both cases. 
     */
    asm volatile    ( "ADD		LR, LR, #4" );

    /* Perform the context switch.  First save the context of the current task. */
    portSAVE_CONTEXT(  );

    /* Find the highest priority task that is ready to run. */
    vTaskSwitchContext(  );

    /* Restore the context of the new task. */
    portRESTORE_CONTEXT(  );
}

/* 
 * The ISR used for the scheduler tick depends on whether the cooperative or
 * the preemptive scheduler is being used.
 */
#if configUSE_PREEMPTION == 0



void
vNonPreemptiveTick( void )
{
    unsigned portLONG ulDummy;

    /* Increment the tick count - which may wake some tasks but as the
     * preemptive scheduler is not being used any woken task is not given
     * processor time no matter what its priority. 
     */
    vTaskIncrementTick(  );

    /* Clear the PIT interrupt. */
    AT91C_BASE_AIC->AIC_EOICR = AT91C_BASE_PITC->PITC_PIVR;
}
#else
void
vPreemptiveTick( void )
{
    /* Save the context of the current task. */
    portSAVE_CONTEXT(  );

    /* Increment the tick count - this may wake a task. */
    vTaskIncrementTick(  );

    /* Find the highest priority task that is ready to run. */
    vTaskSwitchContext(  );

    /* End the interrupt in the AIC. */
    AT91C_BASE_AIC->AIC_EOICR = AT91C_BASE_PITC->PITC_PIVR;;

    portRESTORE_CONTEXT(  );
}
#endif

/*
 * The interrupt management utilities can only be called from ARM mode.  When
 * THUMB_INTERWORK is defined the utilities are defined as functions here to
 * ensure a switch to ARM mode.  When THUMB_INTERWORK is not defined then
 * the utilities are defined as macros in portmacro.h - as per other ports.
 */
void
vPortDisableInterruptsFromThumb( void )
{
    asm volatile ( "STMDB SP!, {R0}\n\t"      /* Push R0. */
                   "MRS   R0, CPSR\n\t"       /* Get CPSR. */
                   "ORR   R0, R0, #0xC0	\n\t" /* Disable IRQ, FIQ. */
                   "MSR   CPSR, R0\n\t"       /* Write back modified value. */
                   "LDMIA SP!, {R0}\n\t"      /* Pop R0. */
                   "BX    R14" );             /* Return back to thumb. */
}

void
vPortEnableInterruptsFromThumb( void )
{
    asm volatile ( "STMDB SP!, {R0}\n\t"      /* Push R0. */
                   "MRS   R0, CPSR\n\t"       /* Get CPSR. */
                   "BIC   R0, R0, #0xC0\n\t"  /* Enable IRQ, FIQ. */
                   "MSR   CPSR, R0\n\t"       /* Write back modified value. */
                   "LDMIA SP!, {R0}\n\t"      /* Pop R0. */
                   "BX    R14" );             /* Return back to thumb. */
}

/* The code generated by the GCC compiler uses the stack in different ways at
 * different optimisation levels.  The interrupt flags can therefore not always
 * be saved to the stack.  Instead the critical section nesting level is stored
 * in a variable, which is then saved as part of the stack context. 
 */
void
vPortEnterCritical( void )
{
    /* Disable interrupts as per portDISABLE_INTERRUPTS(); */
    asm volatile ( "STMDB SP!, {R0}\n\t"      /* Push R0. */
                   "MRS   R0, CPSR\n\t"       /* Get CPSR. */
                   "ORR   R0, R0, #0xC0\n\t"  /* Disable IRQ, FIQ. */
                   "MSR   CPSR, R0\n\t"       /* Write back modified value. */
                   "LDMIA SP!, {R0}" );       /* Pop R0. */

    /* Now interrupts are disabled ulCriticalNesting can be accessed 
     * directly.  Increment ulCriticalNesting to keep a count of how many times
     * portENTER_CRITICAL() has been called. 
     */
    ulCriticalNesting++;
}

void
vPortExitCritical( void )
{
    if( ulCriticalNesting > portNO_CRITICAL_NESTING )
    {
        /* Decrement the nesting count as we are leaving a critical section. */
        ulCriticalNesting--;

        /* If the nesting level has reached zero then interrupts should be
           re-enabled. */
        if( ulCriticalNesting == portNO_CRITICAL_NESTING )
        {
            /* Enable interrupts as per portEXIT_CRITICAL(). */
            asm volatile ( "STMDB SP!, {R0}\n\t"      /* Push R0. */
                           "MRS   R0, CPSR\n\t"       /* Get CPSR. */
                           "BIC   R0, R0, #0xC0\n\t"  /* Enable IRQ, FIQ. */
                           "MSR   CPSR, R0\n\t"       /* Write back modified value. */
                           "LDMIA SP!, {R0}" );       /* Pop R0. */
        }
    }
}
